import copy
from datetime import datetime
import json
from flask import current_app, g
from decorators import Transactional
from enums.yes_no_enum import YesNoEnum
from mauth.login_user_holder import LoginUserHolder
from mauth.mtoken import TokenStrategyFactory
from mexception import AssertTool
from models.user import User
from services import BaseService
from models.user_role import UserRole
from services.enums.user_error_enum import UserErrorEnum
from tools import generate_id, md5_encrypt, random_string
from tools.db_tool import DbTool
from flask_redis import FlaskRedis

redis_client = FlaskRedis()

class UserService(BaseService):
    """
    用户模块业务处理类
    """
    def page(self, form):
        # 重写分页查询，追加roleIds字段
        pageNum = form.data.get('pageNum', 1)
        pageSize = form.data.get('pageSize', 10)
        page = DbTool.filter_by_custom(self.model).filter(getattr(self.model,'adminType')!=1).paginate(pageNum, pageSize, False)
        res = self.model.to_page(page)
        for item in res.get('rows'):
            userRoles = UserRole.query.filter_by(userId=item['id']).all()
            item['roleIds'] = ",".join([str(userRole.roleId) for userRole in userRoles])
        return res
    def info(self):
        """
        获取当前登录用户信息
        :return:
        """
        return LoginUserHolder.get()
    def perm_code(self):
        """
        获取当前登录用户权限码
        :return:
        """
        return LoginUserHolder.get().get('perms')
    
    @Transactional()
    def update_pwd(self, form):
        """
        修改用户个人密码
        :param form:
        :return:
        """
        password = form.get('password')
        newPassword = form.get('newPassword')
        if password == newPassword:
            AssertTool.raise_biz(UserErrorEnum.USER_PWD_REPEAT)
        userId = form.get('id')
        user = User.query.filter_by(id = userId ).first()
        if user is None:
            AssertTool.raise_biz(UserErrorEnum.USER_NOT_EXIST)
        # 旧密码正常，才能修改
        encryptPwd = md5_encrypt(password+user.salt)
        if encryptPwd != user.password:
            AssertTool.raise_biz(UserErrorEnum.PWS_ERROR)
        salt = random_string(8)
        self.db.session.query(User).filter_by(id=userId).update({
            'salt': salt,
            'password': md5_encrypt(newPassword+salt)
        })

    @Transactional()
    def reset_pwd(self, userIds):
        """
        重置用户密码
        :param userIds:
        :return:
        """
        if userIds is None or len(userIds) == 0:
            return
        userList = self.db.session.query(User).filter(User.id.in_(userIds)).filter(User.adminType!= 1).all()
        for user in userList:
            salt = random_string(8)
            default_password = current_app.config.get('M_DEFAULT_PASSWORD', "123456")
            encryptPwd = md5_encrypt(default_password+salt)
            self.db.session.query(User).filter_by(id=user.id).update({
                'salt': salt,
                'password': encryptPwd
            })

    @Transactional()       
    def locked(self, userIds):
        """
        锁定用户
        :param userIds:
        :return:
        """
        if userIds is None or len(userIds) == 0:
            return
        userList = self.db.session.query(User).filter(User.id.in_(userIds)).filter(User.adminType!= 1).all()
        for user in userList:
            self.db.session.query(User).filter_by(id=user.id).update({
                'isLocked': YesNoEnum.YES.code,
            })
    @Transactional()       
    def un_locked(self, userIds):
        """
        取消锁定用户
        :param userIds:
        :return:
        """
        if userIds is None or len(userIds) == 0:
            return
        userList = self.db.session.query(User).filter(User.id.in_(userIds)).filter(User.adminType!= 1).all()
        for user in userList:
            self.db.session.query(User).filter_by(id=user.id).update({
                'isLocked': YesNoEnum.NO.code,
            })
    @Transactional()    
    def grant_role(self, userId, roleIds):
        """
        为用户授予角色
        :param userId:
        :param roleIds:
        :return:
        """
        # 删除用户所有角色关系
        self.db.session.query(UserRole).filter_by(userId=userId).delete()
        if roleIds is None or len(roleIds) == 0:
            return
        for roleId in roleIds:
            userRole = UserRole()
            userRole.id = generate_id()
            userRole.userId = userId
            userRole.roleId = roleId
            self.db.session.add(userRole)
    def online_user_list(self, keywords):
        """
        获取在线用户列表
        """
        tokenKeyPrefix = current_app.config.get('TOKEN_KEY_PREFIX')
        pattern = tokenKeyPrefix + "*"
        redis_client.init_app(current_app)
        keys = list(redis_client.scan_iter(match=pattern))
        userDataList = []
        for key in keys:
            token = key.decode("utf-8").replace(tokenKeyPrefix, "")
            userData = redis_client.get(key)
            if userData is not None:
                userData = json.loads(userData.decode("utf-8"))
                userData['tokenValue'] = token
                userDataList.append(userData)
        if keywords is not None and len(keywords) > 0:
            userDataList = list(filter(lambda x: keywords in x.get('userId'), userDataList))
        tokenDict = {}
        userDataDict = {}
        currentId = LoginUserHolder.get_user_id()
        for userData in userDataList:
            userData['isCurrentUser'] = userData['userId'] == currentId
            userId = userData.get('userId')
            userDataDict[userId] = userData
            tokenList = tokenDict.get(userId)
            if tokenList is None:
                tokenList = [userData]
                tokenDict[userId] = tokenList
            else:
                tokenList.append(userData)
        res = []
        for key, value in tokenDict.items():
            userData = userDataDict.get(key)
            userData['tokenList'] = copy.deepcopy(value)
            time_strings = [t['loginTime'] for t in value]
            datetime_objects = [datetime.strptime(t, "%Y-%m-%d %H:%M:%S") for t in time_strings]
            userData['expireTime'] = max(datetime_objects)
            res.append(userData)
        return res
    def logout_by_token_value(self, tokenValues):
        """
        根据token值强制注销
        :param tokenValues:
        :return:
        """
        if tokenValues is None or len(tokenValues) == 0:
            return
        token_strategy = TokenStrategyFactory.get_instance()
        for token in tokenValues:
            token_strategy.remove(token)
    def kickout_by_token_value(self, tokenValues):
        """
        根据token值踢下线
        :param tokenValues:
        :return:
        """
        self.logout_by_token_value(tokenValues)
    def logout_by_login_id(self, loginIds):
        """
        根据登录ID强制注销
        :param loginIds:
        :return:
        """
        if loginIds is None or len(loginIds) == 0:
            return
        token_strategy = TokenStrategyFactory.get_instance()
        tokenKeyPrefix = current_app.config.get('TOKEN_KEY_PREFIX')
        pattern = tokenKeyPrefix + "*"
        redis_client.init_app(current_app)
        keys = list(redis_client.scan_iter(match=pattern))
        for key in keys:
            token = key.decode("utf-8").replace(tokenKeyPrefix, "")
            userData = redis_client.get(key)
            if userData is not None:
                userData = json.loads(userData.decode("utf-8"))
                userId = userData.get('userId')
                if userId in loginIds:
                    token_strategy.remove(token)
    def kickout_by_login_id(self, loginIds):
        """
        根据登录ID踢下线
        :param loginIds:
        :return:
        """
        self.logout_by_login_id(loginIds)